/*
 * sysmo 服务器用户信息
 * Copyright (C) 2009  YuLicheng
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package org.sysmo.server.user;

import java.security.MessageDigest;
import java.sql.SQLException;
import java.util.List;
import java.util.Vector;
import org.sysmo.db.DatabaseInterface;
import org.sysmo.db.DbUser;
import org.sysmo.server.util.InstanceNotInitializedException;

/**
 * 
 * 从Web端登录用户的用户信息模块。
 * <br />
 *
 * 此类的每个实例对应于一个用户，实例创建后不能更改其与特定用户之间的对应关系。
 * <br />
 *
 * 此类所有具体的数据存储是对数据库接口进行操作。此类会主动使用MD5方式对存入数据库的
 * 密码进行加密。但若所运行的jdk不支持MD5加密，则直接将明码存入数据库。
 * <br />
 *
 * 注意：对用户的所有操作（包括添加删除用户）都必须以一个已经实例化的用户身份进行，因
 * 此系统在部署前必须至少手工在数据库内添加一个用户。
 *
 * @author YuLicheng
 * @see DatabaseInterface
 */
public class User {
    private DatabaseInterface dbInterface;
    private DbUser user;

    /**
     * 根据name参数从数据库中查询相应的用户信息。
     *
     * @param dbInterface 数据库访问接口。
     * @param name 用户名。
     * @throws UserNotExistsException 当所请求的用户名不存在时抛出此异常。
     * @see #setPassword(String)
     */
    public User(DatabaseInterface dbInterface, String name)
            throws UserNotExistsException {
        this.dbInterface = dbInterface;
        try {
            user = dbInterface.query_UserTable(name);
            if (user == null) {
                throw new UserNotExistsException(name);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    /**
     * 根据用户ID从数据库中查询相应的用户信息。
     *
     * @param dbInterface 数据库访问接口。
     * @param id 用户ID。
     * @throws UserNotExistsException 当所请求的用户ID不存在时抛出此异常。
     */
    public User(DatabaseInterface dbInterface, int id)
            throws UserNotExistsException {
        this.dbInterface = dbInterface;
        try {
            user = dbInterface.query_UserTable(id);
            if (user == null) {
                throw new UserNotExistsException(id);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    /**
     * 使用DbUser对象创建User对象。
     *
     * @param dbInterface 数据库接口。
     * @param user DbUser对象。
     */
    public User(DatabaseInterface dbInterface, DbUser user) {
        this.dbInterface = dbInterface;
        this.user = user;
    }

    /**
     * 获取此用户用户名。
     *
     * @return 用户名
     */
    public String getName() {
        if (user == null) {
            throw new InstanceNotInitializedException();
        }
        return user.name;
    }

    /**
     * 获取此用户ID。
     * 若一个用户已经被删除，或未被完全实例化则其ID为-1。
     *
     * @return 该用户的ID。
     */
    public int getID() {
        if (user == null) {
            throw new InstanceNotInitializedException();
        }
        return user.uid;
    }

    /**
     * 验证此用户的密码是否正确。
     *
     * @param password 待验证的密码。
     * @return 若正确返回true，否则返回false。
     */
    public boolean verify(String password) {
        if (user == null) {
            throw new InstanceNotInitializedException();
        }
        return user.password.equals(encodeMD5(password));
    }

    /**
     * 给此用户设置一个新密码。注意，存储在数据库的密码将用MD5方式加密。若运行环境的
     * jdk不支持MD5，则直接将明码存入数据库。
     *
     * @param password 此用户的新密码。
     */
    public void setPassword(String password) {
        if (user == null) {
            throw new InstanceNotInitializedException();
        }
        user.password = encodeMD5(password);
        try{
            dbInterface.update_UserTable(user);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 删除此用户。
     */
    public void remove() {
        if (user == null) {
            throw new InstanceNotInitializedException();
        }
        try {
            dbInterface.delete_UserTable(user.uid);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 以当前用户的身份新增一个用户。
     * 
     * @param name 新用户的用户名。
     * @param password 新用户的密码。
     * @return 新增加的用户对象。
     * @throws UserExistsException 若已存在同名用户则抛出此异常。
     */
    public User addUser(String name, String password)
            throws UserExistsException {
        if (user == null) {
            throw new InstanceNotInitializedException();
        }
        try {
            if (dbInterface.query_UserTable(name) != null) {
                throw new UserExistsException(name);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
        DbUser newUser = new DbUser();
        newUser.name = name;
        newUser.password = encodeMD5(password);
        //TODO: 为DbUser确定新建对象ID。
        newUser.uid = 0;
        try {
            dbInterface.update_UserTable(newUser);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return new User(dbInterface, newUser);
    }

    /**
     * 获取所有用户的列表。
     * 此列表中每一个对象对应于一个已存在的用户。
     *
     * @return 所有用户组成的列表。
     */
    public List<User> getAllUsers() {
        Vector<User> allUsers = null;
        try {
            Vector<DbUser> userSet = dbInterface.query_UserTable_all();
            allUsers = new Vector<User>();
            for (DbUser aUser: userSet) {
                allUsers.add(new User(dbInterface, aUser));
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return allUsers;
    }

    /**
     * 将Byte数组转换为十六进制表示的字符串。
     *
     * @param b Byte数组。
     * @return 相应的十六进制表示的字符串。
     */
    private static String byte2hex(byte[] b) {
        String hs = "";
        String stmp = "";
        for (int n = 0; n < b.length; n++) {
            stmp = Integer.toHexString(b[n] & 0xFF);
            if (stmp.length() == 1) {
                hs = hs + "0" + stmp;
            } else {
                hs = hs + stmp;
            }
        }
        return   hs.toUpperCase();
    }

    /**
     * 将字符串转换为MD5编码。
     *
     * @param str 字符串。
     * @return 相应的MD5编码的结果。
     */
    private static String encodeMD5(String str) {
        try {
            MessageDigest md5 = MessageDigest.getInstance("MD5");
            return byte2hex(md5.digest(str.getBytes("utf-8")));
        } catch (Exception e) {
            e.printStackTrace();
            System.err.println("MD5 not supported!");
            return str;
        }
    }

    public static void main(String[] args) {
        System.out.println(encodeMD5("111111"));
    }
}
